package com.youlian.cloud.gateway.controller;

import com.youlian.cloud.core.response.ResponseCommonModel;
import com.youlian.cloud.core.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.error.ErrorAttributes;
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.context.request.WebRequest;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;


/**
 * zuul网关层面异常捕获处理器
 *
 * @author : liukx
 * @create : 2018/6/26 19:01
 * @email : liukx@elab-plus.com
 */
@RestController
public class ErrorProcessController implements ErrorController {
    private Logger logger = LoggerFactory.getLogger(ErrorProcessController.class);
    private static final String PATH = "/error";
    private final String ERROR_CODE = "ZUUL_ERROR_001";
    private final String TIME_OUT_EXCEPTION = "SocketTimeoutException";
    private final String SERVICE_UNAVAILABLE = "Load balancer does not have available server for client";

    @Autowired
    private ErrorAttributes errorAttributes;


    @RequestMapping(value = PATH, produces = {MediaType.APPLICATION_JSON_VALUE})
    public Object error(HttpServletRequest request, HttpServletResponse response) {
        return errorBody(request, response);
    }

    private ResponseCommonModel errorBody(HttpServletRequest request, HttpServletResponse response) {
        Map<String, Object> errorAttributes = getErrorAttributes(request, true);
        Integer statusCode = (Integer) (errorAttributes.get("status") == null ? 500 : errorAttributes.get("status"));
        String error = (String) errorAttributes.get("error");
        String path = (String) errorAttributes.get("path");
        String message = (String) errorAttributes.get("message");
        String trace = (String) errorAttributes.get("trace");

        statusCode = getStatus(trace, statusCode);
        // 如果是接口超时的情况
        if (statusCode != null && org.apache.http.HttpStatus.SC_INTERNAL_SERVER_ERROR == Integer.valueOf(statusCode)) {
            logger.error(" 内部错误 -  错误消息 : " + error + "路径 : " + path + " 返回消息 : " + message);
        } else {
            logger.info(" 错误码 : " + statusCode + " 错误消息 : " + error + "路径 : " + path + " 返回消息 : " + message);
        }
        ResponseCommonModel responseCommonModel = new ResponseCommonModel();
        responseCommonModel.setSuccess(false);
        responseCommonModel.setErrorCode(statusCode + "");
        responseCommonModel.setMessage(HttpStatus.valueOf(statusCode).name());
        response.setStatus(statusCode);
        return responseCommonModel;
    }

    /**
     * 根据调用链异常信息返回状态码
     *
     * @param trace
     * @param statusCode
     * @return
     */
    private Integer getStatus(String trace, Integer statusCode) {
        if (StringUtils.isNotEmpty(trace)) {
            // 内部转发超时
            if (trace.indexOf(TIME_OUT_EXCEPTION) > 0) {
                statusCode = HttpStatus.GATEWAY_TIMEOUT.value();
            }
            // 服务不可用
            else if (trace.indexOf(SERVICE_UNAVAILABLE) > 0) {
                statusCode = HttpStatus.SERVICE_UNAVAILABLE.value();
            }
        }
        return statusCode;
    }

    @Override
    public String getErrorPath() {
        return PATH;
    }

    private Map<String, Object> getErrorAttributes(HttpServletRequest request, boolean includeStackTrace) {
        ServletWebRequest requestAttributes =  new ServletWebRequest(request);
//        RequestAttributes requestAttributes = new ServletRequestAttributes(request);

        return errorAttributes.getErrorAttributes(requestAttributes, includeStackTrace);
    }

}